﻿using DocBot.Objects;
using DocBot.Util;
using DocBot.Util.Collections;
using DocBot.ViewModel;
using DocBot.Util.Service;

using GalaSoft.MvvmLight;

using Google.YouTube;

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace DocBot.Bot
{
	public class APIManager : ObservableObject
	{
		private static MainWindow _window;
		private static LogViewModel _log;
		private static PlaylistViewModel _playlist;
		private static ChatViewModel _chat;
		private static BlacklistViewModel _blacklist;

		NotifyingQueue<String> _parserQueue = new NotifyingQueue<String>(); // caches parse requests and parses them
		NotifyingQueue<String> _answerQueue = new NotifyingQueue<String>(); // caches reply(added to playlist)
		NotifyingQueue<String> _alreadyParsedQueue = new NotifyingQueue<String>(); // caches reply(already on playlist)
		NotifyingQueue<String> _blacklistedQueue = new NotifyingQueue<String>();

		public APIManager(MainWindow mainwindow)
		{
			_window = mainwindow;
			_log = (LogViewModel)_window.FindResource("LogModel");
			_playlist = (PlaylistViewModel)_window.Playlist.DataContext;
			_chat = (ChatViewModel)_window.Chat.DataContext;
			_blacklist = (BlacklistViewModel)_window.Blacklist.DataContext;
			ParserQueue.Changed += ParserQueue_Changed;
		}

		private void ParserQueue_Changed(object sender, EventArgs e)
		{
			if (ParserQueue != null)
				if (ParserQueue.Count > 0)
				{
					BackgroundWorker bgw = new BackgroundWorker();
					bgw.DoWork += bgw_DoWork;
					bgw.RunWorkerAsync();
				}
		}

		void bgw_DoWork(object sender, DoWorkEventArgs e)
		{
			parse(ParserQueue.Dequeue());
		}

		public NotifyingQueue<String> BlacklistedQueue
		{
			get { return _blacklistedQueue; }
			set { _blacklistedQueue = value; }
		}

		public NotifyingQueue<String> AlreadyParsedQueue
		{
			get { return _alreadyParsedQueue; }
			set { _alreadyParsedQueue = value; }
		}

		public NotifyingQueue<String> AnswerQueue
		{
			get { return _answerQueue; }
			set { _answerQueue = value; }
		}

		public NotifyingQueue<String> ParserQueue
		{
			get { return _parserQueue; }
			set { _parserQueue = value; }
		}

		private void parse(string item)
		{
			int count = 0;
			// url parser for youtube
			string paturl = @"(?:http|https|)(?::\/\/|)(?:www.|m.|)(?:youtu\.be\/|youtube\.com(?:\/embed\/|\/v\/|\/watch\?v=|\|\/feeds\/api\/videos\/|\/user\S*[^\w\-\s]|\S*[^\w\-\s]))([\w\-]*)[a-z0-9;:@?&%=+\/\$_.-]*";
			Match _match = Regex.Match(item, paturl);
			while (_match.Success)
			{
				count++;
				String temp = _match.Value;
				//special case youtu.be
				if (temp.Contains(@"youtu.be/"))
				{
					temp = temp.Replace(@"youtu.be/", @"youtube.com/watch?v=");
				}
				YoutubeAPI api = new YoutubeAPI();
				foreach (string o in System.Web.HttpUtility.ParseQueryString(new Uri(temp).Query).AllKeys)
				{
					string id = String.Empty;
					switch (o)
					{
						case "v":
							id = System.Web.HttpUtility.ParseQueryString(new Uri(temp).Query).Get(o);
							GetYoutubeTrackMetaData(id);
							break;
						case "list":
							if (Settings.Instance.Valid_Youtube_Mail)
							{
								List<PlaylistItem> list = new List<PlaylistItem>();
								id = System.Web.HttpUtility.ParseQueryString(new Uri(temp).Query).Get(o);
								list = api.GetPlayListSongs(id);
								foreach (PlaylistItem p in list)
								{
									GetYoutubeTrackMetaData(id = System.Web.HttpUtility.ParseQueryString(new Uri(p.Url).Query).Get("v"));
								}
							}
							break;
					}
				}
				_match = _match.NextMatch();
			}
			if (count == 0)
				Messenger.Instance.Messages.Enqueue(new Message("Nothing has been found in the requested URL", MessageType.LogAndChat));
		}

		#region Youtube
		/// <summary>
		/// function to get metadata from a video using the youtube url
		/// </summary>
		/// <param name="id">the normalized youtube url</param>
		/// <returns>the video object containing the data</returns>
		private void GetYoutubeTrackMetaData(String id)
		{
			const string gdata = @"https://gdata.youtube.com/feeds/api/videos/";
			const string yturl = @"https://www.youtube.com/watch?v=";

			Video video = new Video();

			try
			{
				if (id == null || id == String.Empty) throw new Exception("Supplied video id is empty");
				YouTubeRequest request = new YouTubeRequest(new YouTubeRequestSettings(null, null, null));
				Uri videoEntryUrl = new Uri(gdata + id);
				video = request.Retrieve<Google.YouTube.Video>(videoEntryUrl);

				if (video != null)
				{
					PlaylistItem item = new PlaylistItem
					{
						ArtistName = video.Author,
						SongTitle = video.Title,
						Type = PlayListItemType.Youtube,
						Url = yturl + video.VideoId,
						PlayCount = 0,
						Duration = video.Media.Duration.IntegerValue
					};
					if(_blacklist.check(item.SongTitle,BlackListItemType.Keyword) ||_blacklist.check(item.Url,BlackListItemType.PlaylistItem ))
					{
						BlacklistedQueue.Enqueue(item.SongTitle);
						return;
					}
					if (_playlist.checkSongs(item.Url))
					{
						AlreadyParsedQueue.Enqueue(item.SongTitle);
						return;
					}

					item.Restricted = checkYoutubeRestrictions(video);
					
					_playlist.Songs.Add(item);
					AnswerQueue.Enqueue(video.Title);
					Messenger.Instance.Messages.Enqueue(new Message(item.SongTitle + " has been added to the playlist", MessageType.Log));
				}

			}
			catch (System.IO.FileLoadException x)
			{
				if (Settings.Instance.DebugMode)
					Messenger.Instance.Messages.Enqueue(new Message("GetYoutubeTrackMetaData: " + x.Message + " @" + x.Source.ToString(), MessageType.Log));
			}
			catch (Exception e)
			{
				if (Settings.Instance.DebugMode)
					Messenger.Instance.Messages.Enqueue(new Message("GetYoutubeTrackMetaData: " + e.Message + " @" + e.Source.ToString(), MessageType.Log));
			}
		}

		/// <summary>
		/// checks if a video is restricted and therefor cant be played
		/// </summary>
		/// <param name="pVideo">the video object containing the data</param>
		/// <returns>true if yes, false if no</returns>
		private bool checkYoutubeRestrictions(Google.YouTube.Video pVideo)
		{
			if (pVideo.Status != null)
			{
				if (pVideo.Status.Reason.Equals("requesterRegion"))
				{
					return true;
				}
			}
			return false;
		}
		#endregion
	}
}
